﻿using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Data.Entity.Design;
using System.Data.Metadata.Edm;
using System.Linq;
using System.Text;

namespace EntityFrameworkContrib.Design.VisualStudio.Testing.Mock
{
	public class MockCodeGenerator : IEdmxCodeGeneratorSubscriber
	{
		public static bool Subscribed { get; set; }

		public static List<GlobalItem> Items
		{
			get { return items; }
		}	readonly static List<GlobalItem> items = new List<GlobalItem>();
		
		public void Subscribe( EntityClassGenerator generator )
		{
			Items.Clear();
			Subscribed = true;
			generator.OnTypeGenerated += OnTypeGenerated;
			generator.OnPropertyGenerated += OnPropertyGenerated;
		}

		/// <summary>
		/// This event is raised by code generation after a type is generated
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="eventArgs"></param>
		void OnTypeGenerated( object sender, TypeGeneratedEventArgs eventArgs )
		{
			Items.Add( eventArgs.TypeSource );
			eventArgs.AdditionalAttributes.AddRange( CreateCodeAttributes( eventArgs.TypeSource ) );
		}

		/// <summary>
		/// This event is raised by code generation after a property is generated
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="eventArgs"></param>
		void OnPropertyGenerated( object sender, PropertyGeneratedEventArgs eventArgs )
		{
			eventArgs.AdditionalAttributes.AddRange( CreateCodeAttributes( eventArgs.PropertySource ) );
		}

		/// <summary>
		/// CSDL annotations show up as metadata properties in EDM
		/// This method uses the metadata APIs to look for CSDL annotations that are in our custom namespace and
		/// creates and returns CodeAttributeDeclarations with the necessary parameters
		/// </summary>
		/// <param name="item"></param>
		/// <returns>List of CodeAttributeDeclarations</returns>
		IList<CodeAttributeDeclaration> CreateCodeAttributes( MetadataItem item )
		{
			string xmlns = "http://tempuri.org/SampleAnnotations";

			List<CodeAttributeDeclaration> codeAttributeDeclarations = new List<CodeAttributeDeclaration>();
			if ( item != null )
			{
				// Get our CSDL annotations from EDM metadata
				IEnumerable<MetadataProperty> metadataProperties = item.MetadataProperties.Where( prop => prop.Name.StartsWith( xmlns ) );
				foreach ( MetadataProperty metadataProperty in metadataProperties )
				{
					string metadataPropertyValue = (string)metadataProperty.Value;
					if ( !String.IsNullOrEmpty( metadataPropertyValue ) )
					{
						// We may have multiple attributes delimited by a ';'
						string[] attributes = metadataPropertyValue.Split( new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries );
						foreach ( string attribute in attributes )
						{
							string attributeName = attribute;
							string[] attributeParams = new string[1];

							// Get attribute parameters between '(' and ')'
							if ( attribute.Contains( '(' ) )
							{
								attributeParams = attribute.Split( new char[] { '(', ')' }, StringSplitOptions.RemoveEmptyEntries );
								attributeName = attributeParams[0];
							}

							// Create CodeDom attribute
							CodeAttributeDeclaration codeAttributeDeclaration = new CodeAttributeDeclaration( attributeName );

							// Create CodeDom attribute parameters
							// NOTE: this sample only handles string and bool attribute parameters
							foreach ( string attributeParam in attributeParams.Skip( 1 ) )
							{
								bool attributeParamBool;
								object attributeParamObj = bool.TryParse( attributeParam, out attributeParamBool ) ? (object)attributeParamBool : attributeParam;
								codeAttributeDeclaration.Arguments.Add(
									new CodeAttributeArgument( new CodePrimitiveExpression( attributeParamObj ) ) );
							}
							codeAttributeDeclarations.Add( codeAttributeDeclaration );
						}
					}
				}
			}
			return codeAttributeDeclarations;
		}
	}
}
